#include <common/logger/logger.h>
#include <common/stringverify/stringverify.h>
#include <iomanip>
#include <regex>
#include <sstream>

/* 关于逻辑中的长度限制：
 * 安全性考虑（防止异常或攻击）
 * 在理论上，正则表达式、字符串处理函数和日期解析等都可以处理任意长度的字符串，但在现实中：
 * 正则引擎性能问题：超长字符串（比如几十万甚至几百万字符）可能导致 正则回溯过多，甚至程序卡死或崩溃。
 * 内存消耗：处理极长字符串时会占用大量内存，可能触发 缓冲区溢出、堆栈耗尽 或其他安全风险。
 * DoS 攻击风险：如果系统是网络服务，攻击者可能通过发送超长邮箱、URL 或电话号码尝试 拒绝服务攻击。
 *
 * 有些标准和实践确实对长度有限制：
 * -------------------------------------------------------------------------
 * 类型			| 常见限制
 * -------------|-----------------------------------------------------------
 * Email 地址	| RFC 5321: 总长度 ≤ 256，用户名 ≤ 64，域名 ≤ 255
 * -------------|-----------------------------------------------------------
 * URL			| 一般浏览器支持的最大 URL 长度 ≈ 2000-2083 字符（IE 规定）
 * -------------|-----------------------------------------------------------
 * 手机号码		| 固定长度，一般 11 位（中国大陆）
 * -------------|-----------------------------------------------------------
 * 字母数字字段	| 一般业务规则决定，如用户名 ≤ 50
 * -------------------------------------------------------------------------
 */

#include <algorithm>
#include <cctype>
#include <ctime>
#include <iomanip>
#include <regex>
#include <sstream>
#include <string>

// 辅助函数放在匿名命名空间，避免外部污染
namespace
{
	inline std::string trim(const std::string& s)
	{
		size_t a = s.find_first_not_of(" \t\r\n");
		if (a == std::string::npos) return "";
		size_t b = s.find_last_not_of(" \t\r\n");
		return s.substr(a, b - a + 1);
	}

	// 仅保留数字字符
	inline std::string digitsOnly(const std::string& s)
	{
		std::string out;
		out.reserve(s.size());
		for (char c: s)
		{
			if (std::isdigit(static_cast<unsigned char>(c))) out.push_back(c);
		}
		return out;
	}

	// 使用 std::get_time 解析后，用 mktime 规范化并比对年月日，保证日期有效（例如闰年检查）
	inline bool parseAndValidateDate(const std::string& s, const char* fmt)
	{
		std::tm tm = {};
		std::istringstream ss(s);
		ss >> std::get_time(&tm, fmt);
		if (ss.fail()) return false;

		// mktime 可能会对非法日期进行“归一化”，所以调用后比对字段是否一致
		std::tm copy = tm;
		// 使用 mktime 进行规范化（注意 mktime 以本地时间解释）
		time_t t = std::mktime(&copy);
		if (t == -1)
		{
			// mktime 失败 -> 当作非法
			return false;
		}
		// 比对 年/月/日 是否一致（tm_year: years since 1900, tm_mon: 0-11）
		if (copy.tm_year == tm.tm_year && copy.tm_mon == tm.tm_mon && copy.tm_mday == tm.tm_mday)
		{
			return true;
		}
		return false;
	}
}// namespace

bool StringVerify::isEmailAddr(const std::string& emailStr)
{
	if (emailStr.empty() || emailStr.size() > 320)
	{
		return false;
	}// 最大长度限制（RFC 等建议）
	static const std::regex pattern(
			R"(^([0-9A-Za-z]([-.\w]*[0-9A-Za-z])*@([0-9A-Za-z][-\w]*[0-9A-Za-z]\.)+[A-Za-z]{2,63})$)",
			std::regex::ECMAScript | std::regex::icase);
	try
	{
		return std::regex_match(emailStr, pattern);
	}
	catch (const std::regex_error&)
	{
		return false;
	}
}

/*
 *电话号码的匹配和CSharp不完全一致，简单来说，CSharp兼容性更强，但不确定是否正确，例如C#下：13812345678ext123 -> True
 */
bool StringVerify::isPhoneNumber(const std::string& phoneStr)
{
	std::string s = trim(phoneStr);
	if (s.empty() || s.size() > 50)
	{
		return false;
	}

	// 国际号码：+ 开头
	if (s[0] == '+')
	{
		// + 后必须是纯数字，不允许符号
		std::string rest = s.substr(1);
		if (!std::all_of(rest.begin(), rest.end(), ::isdigit))
		{
			return false;
		}
		return rest.size() >= 7 && rest.size() <= 15;
	}

	// 国际号码：00 开头
	if (s.size() >= 2 && s[0] == '0' && s[1] == '0')
	{
		std::string rest = s.substr(2);
		if (!std::all_of(rest.begin(), rest.end(), ::isdigit))
		{
			return false;
		}
		return rest.size() >= 7 && rest.size() <= 15;
	}

	// 本地/传统格式（保留连字符 - 的情况）
	// 支持：
	//  - 11 位纯数字（如中国手机号）
	//  - 7-8 位纯数字（本地座机）
	//  - 区号-号码 (3-4位区号)-(7-8位号码)
	//  - 区号-号码-分机 (3-4)-(7-8)-(\d{1,4})
	//  - 号码-分机 (7-8)-(\d{1,4})
	static const std::regex localPattern(
			R"(^(\d{11}|\d{7,8}|\d{3,4}-\d{7,8}|\d{3,4}-\d{7,8}-\d{1,4}|\d{7,8}-\d{1,4})$)");
	try
	{
		if (std::regex_match(s, localPattern)) return true;
	}
	catch (const std::regex_error&)
	{
		return false;
	}

	return false;
}


bool StringVerify::isDateTime(const std::string& dateTimeStr)
{
	std::string s = trim(dateTimeStr);
	if (s.empty())
	{
		return false;
	}

	// 尝试多个常见格式，解析并严格验证（包括闰年）
	const char* formats[] = {"%Y-%m-%d %H:%M:%S", "%Y/%m/%d %H:%M:%S", "%Y-%m-%d", "%Y/%m/%d"};
	for (auto fmt: formats)
	{
		if (parseAndValidateDate(s, fmt)) return true;
	}
	return false;
}

bool StringVerify::isUrl(const std::string& urlStr)
{
	if (urlStr.empty() || urlStr.size() > 2083)
	{
		return false;// IE 最大 URL 长度参考
	}

	try
	{
		// 更严格：只支持 http / https，主机允许 localhost / 域名 / IPv4，端口 1-5 位可选，路径不允许空格
		static const std::regex urlRegex(
				R"(^(https?://)((localhost)|((\d{1,3}\.){3}\d{1,3})|([\w\-]+\.)+[\w\-]+)(:\d{1,5})?(/[^\s#]*)?(#[^\s]*)?$)",
				std::regex::icase);
		return std::regex_match(urlStr, urlRegex);
	}
	catch (const std::regex_error&)
	{
		return false;
	}
}

bool StringVerify::isAlphanumeric(const std::string& str)
{
	// NOTE:
	// 根据Charp的结果：空字符串被视为合法（返回 true）。
	// 如果以后改规则想把空串当非法，只需把下面一行改为 `if (str.empty()) return false;`
	if (str.empty())
	{
		// 空串视为合法：返回 true
		return true;
	}
	for (char ch: str)
	{
		if (!std::isalnum(static_cast<unsigned char>(ch))) return false;
	}
	return true;
}

bool StringVerify::isNumber(const std::string& str)
{
	for (const char& ch: str)
		if (!isdigit(ch) && ch != '.' && ch != '-')
			return false;
	return true;
}
